package com.muyu.danmo.utils.aspect;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.muyu.danmo.utils.annotation.AutoRebuildCache;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Aspect
@Component
public class AutoRebuildCacheAspect {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private static final String META_PREFIX = "cache:meta:";

    private final ExpressionParser parser = new SpelExpressionParser();
    private final ParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();

    @PostConstruct
    public void init() {
        log.info("AutoRebuildCacheAspect 初始化开始");
    }

    @Around("@annotation(autoRebuildCache)")
    public Object around(ProceedingJoinPoint joinPoint, AutoRebuildCache autoRebuildCache) throws Throwable {
        String cacheName = autoRebuildCache.cacheName();
        String keyExpr = autoRebuildCache.key();
        long ttl = autoRebuildCache.ttl();
        Object[] args = joinPoint.getArgs();

        // 获取方法信息
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();

        // 构建 SpEL 上下文
        MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(
                new TypedValue(null),
                method,
                args,
                nameDiscoverer
        );

        // 解析 SpEL 表达式
        Object keyObj;
        try {
            keyObj = parser.parseExpression(keyExpr).getValue(context);
        } catch (SpelEvaluationException e) {
            log.error("SpEL 解析失败，表达式: {}, 错误信息: {}", keyExpr, e.getMessage());
            throw new IllegalArgumentException("无效的 SpEL 表达式: " + keyExpr, e);
        }

        // 生成缓存键
        String evaluatedKey = (keyObj != null) ? keyObj.toString() : "null";

        String realKey = "cache:" + cacheName + "::" + DigestUtils.md5DigestAsHex(evaluatedKey.getBytes());

        // 查询缓存
        Object cached = redisTemplate.opsForValue().get(realKey);
        if (cached != null) {
            log.info("缓存命中，key: {} value:{}", realKey, cached);
            return cached;
        }

        // 执行目标方法
        Object result = joinPoint.proceed();

        // 存入缓存
        if (result != null) {
            long randomSeconds = RandomUtil.randomLong(50, 300);
            redisTemplate.opsForValue().set(realKey, result, Duration.ofSeconds(ttl + randomSeconds));

            // 缓存方法元数据，用于自动重建
            Map<String, Object> meta = new HashMap<>();
            meta.put("class", joinPoint.getTarget().getClass().getName());
            meta.put("method", method.getName());
            meta.put("args", args);
            meta.put("ttl", ttl);
            meta.put("keyExpr", keyExpr);

            redisTemplate.opsForHash().put(META_PREFIX + cacheName, realKey, JSONUtil.toJsonStr(meta));
        }

        return result;
    }
}

